import struct

#Constants
HEADER_FILE='alike_masks.h'
OBJ_FILE='alike_masks.c'

PREFIX_8="__U8_ALIKE_"
PREFIX_16="__U16_ALIKE_"
PREFIX_32="__U32_ALIKE_"
PREFIX_64="__U64_ALIKE_"

def generate_header_header(f):
    t = "/* This Source Code Form is subject to the terms of the Mozilla Public\n"
    t += " * License, v. 2.0. If a copy of the MPL was not distributed with this\n"
    t += " * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n\n"
    t += "#ifndef ALIKE_MASKS\n"
    t += "#define ALIKE_MASKS\n\n"
    t += "#include <inttypes.h>\n"
    t += "#include \"endianness.h\"\n\n"
    t += "/* This file is autogenerated. Do not modify it */\n\n"
    f.write(t)


def generate_array_decls(f):
    t = "/* 8 bit types */\n"
    t += "extern const uint8_t __u8_alike_masks[8];\n";
    t += "/* 16 bit types */\n"
    t += "extern const uint16_t __u16_alike_masks[16];\n";
    t += "/* 32 bit types */\n"
    t += "extern const uint32_t __u32_alike_masks[32];\n";
    t += "/* 64 bit types */\n"
    t += "extern const uint64_t __u64_alike_masks[64];\n\n";
    f.write(t)

def val_to_little_endian(bits, val):
    if bits == 8:
        return int(val)

    if bits == 16:
        val_ = struct.pack('>H', int(val))
        return struct.unpack('H', val_)[0]

    if bits == 32:
        val_ = struct.pack('>I', int(val))
        return struct.unpack('I', val_)[0]

    if bits == 64:
        val_ = struct.pack('>Q', val)
        return struct.unpack('Q', val_)[0]

    raise Exception("Unsupported number of bits")

def generate_constants(bits, be):
    t = ""
    for i in range(0, bits):

        #Construct the string of bits
        bin_str=""
        for j in range(0, bits):
            if j > i:
                bin_str +="0"
            else:
                bin_str +="1"

        #Generate a long (64bit)
        val = long(bin_str, 2)

        #Transform to Little endian
        if not be:
            val = val_to_little_endian(bits, val);

        if bits <= 32:
            val = int(val)

        #Define constant
        t += "#define "+eval("PREFIX_%d" % bits)+str(i+1)+"\t0x"
        if be:
            t += format(val, 'X')
        else:
            t += format(val, 'X')[:bits/4]

        #64 bit type => add ULL (backwards compatibility for 32bit systems)
        if bits == 64:
            t+="ULL"
        t +="\n"
    t +="\n"
    return t

def generate_header_big_endian(f):
    t = "/* Endianess-independent bit mask constants */\n\n"

    #u8
    t += "/* uint8_t */\n"
    t += generate_constants(8, True)

    t += "#ifdef BIG_ENDIAN_DETECTED\n\n"

    #u16
    t += "/* uint16_t */\n"
    t += generate_constants(16, True)

    #u32
    t += "/* uint32_t */\n"
    t += generate_constants(32, True)

    #u64
    t += "/* uint64_t */\n"
    t += generate_constants(64, True)

    f.write(t)


def generate_header_little_endian(f):
    t = "#else /* ENDIANNESS */\n\n"
    #u16
    t += "/* uint16_t */\n"
    t += generate_constants(16, False)

    #u32
    t += "/* uint32_t */\n"
    t += generate_constants(32, False)

    #u64
    t += "/* uint64_t */\n"
    t += generate_constants(64, False)

    f.write(t)


def generate_header_footer(f):
    t = "#endif /* ENDIANNESS */\n\n"
    t += "#endif /* ALIKE_MASKS */\n"
    f.write(t)


def generate_obj_array_pos(bits):
    t = ""
    for i in range(1,bits+1):
        t += "\t"+eval("PREFIX_%d" % bits)+str(i)
        if i != bits+1:
            t+=","
        t+="\n"
    return t

def generate_obj_file(f):
    t = "#include \""+HEADER_FILE+"\"\n\n"

    #u8
    t += "const uint8_t __u8_alike_masks[8] = {\n";
    t += generate_obj_array_pos(8)
    t += "};\n\n";

    #u16
    t += "const uint16_t __u16_alike_masks[16] = {\n";
    t += generate_obj_array_pos(16)
    t += "};\n\n";

    #u32
    t += "const uint32_t __u32_alike_masks[32] = {\n";
    t += generate_obj_array_pos(32)
    t += "};\n\n";

    #u64
    t += "const uint64_t __u64_alike_masks[64] = {\n";
    t += generate_obj_array_pos(64)
    t += "};\n\n";

    f.write(t)

#
#Main routine
#

#HEADER
h = open(HEADER_FILE,'w')

generate_header_header(h)
generate_array_decls(h)
generate_header_big_endian(h)
generate_header_little_endian(h)
generate_header_footer(h)

h.close()

#OBJ FILE
o = open(OBJ_FILE,'w')

#Object file
generate_obj_file(o)

o.close()

